package com.lin.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.lin.po.AddressPO;
import com.lin.po.UserPO;
import com.lin.mapper.UserPOMapper;
import com.lin.service.UserService;
import com.lin.vo.AddressVO;
import com.lin.vo.UserVO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * UserService接口继承了IService,而UserService接口实现类
 * 又继承了IService实现类ServiceImpl, !!!ServiceImpl中已经注入了mapper
 * <p><code>@Autowired<br>protected M baseMapper;</code></p>
 * 所以, 在UserServiceImpl中, 不需要再注入UserPOMapper了, 直接使用即可(baseMapper)
 * <p>
 * 也可以使用userService, 同上所说, 当前类继承了IService的实现类,
 * 可以用this调用其方法(也可省略, 但建议加上便于阅读)
 * </p>
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserPOMapper, UserPO> implements UserService {
    private final Map<String, Method> methodCache = new ConcurrentHashMap<>();

    @Value("${queriedColumns.user.selectPartialFieldByNameAndBalance2:}")
    private String queriedColumns;

    /**
     * 通过id查询用户基本信息以及收货地址信息
     * @param id 用户id
     * @return 用户视图
     */
    @Override
    public UserVO getUserAndAddrById(Long id) {
        // 1. 查询用户信息
        UserPO userPO = getById(id);
        if (userPO == null || userPO.getStatus() != 1) {
            throw new RuntimeException("User status is not normal");
        }

        // 2. 查询地址信息
        List<AddressPO> addrList = Db.lambdaQuery(AddressPO.class)
                .eq(AddressPO::getUserId, id).list();

        // 3. 封装VO
        UserVO userVO = BeanUtil.copyProperties(userPO, UserVO.class);
        if (CollUtil.isNotEmpty(addrList)) {
            // userVO的address属性需要addressVO类型, 但现在是PO, 需要转换一下
            userVO.setAddresses(BeanUtil.copyToList(addrList, AddressVO.class));
        }
        return userVO;
    }

    /**
     * 根据id列表查询用户信息以及地址信息
     * 这种性能较差, 在循环内一个一个查询
     * @param ids 用户id列表
     * @return 用户视图列表
     */
    @Override
    public List<UserVO> getUsersAndAddrByIds(List<Long> ids) {
        List<UserVO> userVOList = new ArrayList<>();
        // 1. 获取用户列表
        List<UserPO> userPOList = listByIds(ids);
        // 2. 根据用户id查询地址信息
        for (UserPO userPO : userPOList) {
            if (userPO == null || userPO.getStatus() != 1) {
                throw new RuntimeException("User status is not normal");
            }
            List<AddressPO> addressPOList = Db.lambdaQuery(AddressPO.class)
                    .eq(AddressPO::getUserId, userPO.getId()).list();
            // 3. 封装VO
            UserVO userVO = BeanUtil.copyProperties(userPO, UserVO.class);
            if (CollUtil.isNotEmpty(addressPOList)) {
                // userVO的address属性需要addressVO类型, 但现在是PO, 需要转换一下
                userVO.setAddresses(BeanUtil.copyToList(addressPOList, AddressVO.class));
            }
            userVOList.add(userVO);
        }
        return userVOList;
    }

    @Override
    public List<UserVO> getUsersAndAddrByIds2(List<Long> ids) {
        // 1. 查询用户
        List<UserPO> userPOList = listByIds(ids);
        if (CollUtil.isNotEmpty(userPOList)) return Collections.emptyList();

        // 2. 查询地址
        // 2.1 查询所有用户id
        List<Long> userIds = userPOList.stream().map(UserPO::getId).toList();

        // 2.2 根据用户id查询地址
        List<AddressPO> addressPOList = Db.lambdaQuery(AddressPO.class)
                .in(AddressPO::getUserId, userIds).list();

        // 2.3 转换地址VO
        List<AddressVO> addressVOS = BeanUtil.copyToList(addressPOList, AddressVO.class);

        // 2.4 根据地址id分组, 相同用户id放到同一个集合
        Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);
        if (CollUtil.isNotEmpty(addressVOS)) {
            addressMap = addressVOS.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
        }

        // 3. 封装VO
        List<UserVO> userVOList = new ArrayList<>(userPOList.size()); // 提前分配好空间, 避免扩容
        for (UserPO userPO : userPOList) {
            UserVO userVO = BeanUtil.copyProperties(userPO, UserVO.class);
            userVOList.add(userVO);
            userVO.setAddresses(addressMap.get(userVO.getId())); // 通过用户id在map中获取地址信息
        }

        return userVOList;
    }

    /**
     * 通过lambdaQuery简化复杂条件查询
     *
     * @param name       姓名
     * @param status     状态
     * @param minBalance 最小余额
     * @param maxBalance 最大余额
     * @return 匹配用户信息
     */
    @Override
    public List<UserPO> queryByCondition(String name, Integer status, Integer minBalance, Integer maxBalance) {
        return lambdaQuery()
                .like(name != null, UserPO::getUsername, name)
                .eq(status != null, UserPO::getStatus, status)
                .ge(minBalance != null, UserPO::getBalance, minBalance)
                .le(maxBalance != null, UserPO::getBalance, maxBalance)
                .list();
    }

    /**
     * 通过lambdaUpdate简化复杂条件更新
     * @param id   用户id
     * @param money 更新金额
     */
    @Override
    public void updateByCondition(Integer id, Integer money) {
        // 1. 获取用户信息
        UserPO userPO1 = getById(id);
        // 2. 判断用户状态是否正常
        if (userPO1 == null || userPO1.getStatus() != 1) throw new RuntimeException("User status is not normal");
        // 3. 判断用户余额是否大于扣减金额
        if (userPO1.getBalance() < money) throw new RuntimeException("User balance is not enough");
        // 4. 预定义扣减后的余额
        int remainingBalance = userPO1.getBalance() - money;
        // 5. 执行更新操作
        lambdaUpdate()
                .set(remainingBalance >= 0, UserPO::getBalance, remainingBalance)
                .set(remainingBalance <= 0, UserPO::getStatus, 2)
                .eq(UserPO::getId, id) // 设置条件
                // 乐观锁, 检查当前余额和之前获取的余额是否相等, 并发可能会出问题, 也可以用悲观锁, synchronized, lock
                .eq(UserPO::getBalance, userPO1.getBalance())
                .update(); // 执行更新操作
    }

    /**
     * 根据id减少余额
     *
     * @param id         用户id
     * @param minBalance 扣减金额
     */
    @Override
    public void deductBalanceById(Integer id, Integer minBalance) {
        // 判断用户状态是否正常
        UserPO userPO = baseMapper.selectById(id);
        if (userPO == null || userPO.getStatus() != 1) throw new RuntimeException("User status is not normal");
        // 判断用户余额是否大于扣减金额
        if (userPO.getBalance() < minBalance) throw new RuntimeException("User balance is not enough");
        // 满足条件, 执行扣减操作
        baseMapper.deductBalanceById(id, minBalance);
    }

    @Override
    public void selectPartialFieldByNameAndBalance(Integer miniBalance) throws InvocationTargetException, IllegalAccessException {
        LambdaQueryWrapper<UserPO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        List<String> queriedFields = new ArrayList<>();

        // 通过反射获取User的所有属性名
        Field[] declaredFields = UserPO.class.getDeclaredFields();
        List<String> fieldNames = Arrays.stream(declaredFields).map(Field::getName).toList();

        // 判断配置文件是否为空
        if (queriedColumns.isBlank()) {
            queriedFields.addAll(fieldNames);
        } else {
            // 获取配置文件中定义的要查询的属性名
            List<String> qcs = Arrays.stream(queriedColumns.split(",")).map(String::trim).filter(s -> !s.isBlank())  // 过滤空字符串
                    .toList();

            // 判断配置文件中的属性名是否在User的所有属性名中
            for (String qc : qcs) {
                if (fieldNames.contains(qc)) queriedFields.add(qc);
            }
        }

        // 构造查询条件
        lambdaQueryWrapper.select(UserPO.class, s -> queriedFields.contains(s.getProperty())).ge(UserPO::getBalance, miniBalance);

        // 执行查询
        List<UserPO> userPOS = baseMapper.selectList(lambdaQueryWrapper);

        // 预先获取Method方法对象
        Map<String, Method> methodMap = queriedFields.stream().collect(Collectors.toMap(filed -> filed, filed -> {
            try {
                return UserPO.class.getMethod("get" + StringUtils.capitalize(filed));
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }));

        // users中可能有些列为null, 因为只查询了部分字段, 只返回非null字段, 通过动态反射获取
        for (UserPO userPO : userPOS) {
            for (String queriedField : queriedFields) {
                // 遍历queriedFields, 获取字段名, 然后反射获取对应get方法
                System.out.print(methodMap.get(queriedField).invoke(userPO) + "\t");
                /* 这种方式效率较低, 每次循环都要反射获取方法对象
                String fieldName = "get" + StringUtils.capitalize(queriedField); // 拼接方法名
                Method method = user.getClass().getDeclaredMethod(fieldName); // 获取方法
                Object invoke = method.invoke(user); // 反射调用get方法
                System.out.print(invoke + "\t");*/
            }
            System.out.println();
        }
    }

    /**
     * 根据id批量减少余额
     *
     * @param wrapper 查询条件
     * @param balance 操作金额
     */
    @Override
    public void minusBalanceByIds(LambdaQueryWrapper<UserPO> wrapper, Integer balance) {
        if (balance == null || balance <= 0) throw new IllegalArgumentException("Invalid balance value");
        baseMapper.minusBalanceByIds(wrapper, balance);
    }

    @Override
    public void minusBalanceByIds(Integer balance) {
        if (balance == null || balance <= 0) throw new IllegalArgumentException("Invalid balance value");
        baseMapper.minusBalanceByIds(null, balance);
    }

    /**
     * 根据id批量增加余额
     *
     * @param wrapper 查询条件
     * @param balance 操作金额
     */
    @Override
    public void addBalanceByIds(LambdaQueryWrapper<UserPO> wrapper, Integer balance) {
        if (balance == null || balance <= 0) throw new IllegalArgumentException("Invalid balance value");
        baseMapper.addBalanceByIds(wrapper, balance);
    }

    @Override
    public void addBalanceByIds(Integer balance) {
        if (balance == null || balance <= 0) throw new IllegalArgumentException("Invalid balance value");
        baseMapper.addBalanceByIds(null, balance);
    }

    /**
     * 根据条件查询用户信息
     *
     * @param minBalance 最低余额
     * @return 符合条件的用户信息
     */
    private List<UserPO> queryUsersWithCondition(int minBalance) {
        if (minBalance <= 0) throw new IllegalArgumentException("balance value must be greater than zero");
        LambdaQueryWrapper<UserPO> wrapper = buildQueryWrapper(minBalance);
        initializeMethodCache();
        return baseMapper.selectList(wrapper);
    }

    /**
     * 构建查询条件
     *
     * @return LambdaQueryWrapper<User>
     */
    private LambdaQueryWrapper<UserPO> buildQueryWrapper(Integer minBalance) {
        LambdaQueryWrapper<UserPO> wrapper = new LambdaQueryWrapper<>();
        List<String> fields = resolveQueryFields();

        wrapper.select(UserPO.class, info -> fields.contains(info.getProperty())).ge(UserPO::getBalance, minBalance);
        return wrapper;
    }

    /**
     * 解析查询字段 -- 通过反射获取实体类的所有属性
     *
     * @return List<String>
     */
    private List<String> resolveQueryFields() {
        if (queriedColumns.isBlank()) {
            return Arrays.stream(UserPO.class.getDeclaredFields()).map(Field::getName).collect(Collectors.toList());
        }

        return Arrays.stream(queriedColumns.split(",")).map(String::trim).filter(s -> !s.isBlank()).filter(this::isValidField) // 这里要抛异常, 写在这里会让该方法太长, 提取为一个方法
                .collect(Collectors.toList());
    }

    /**
     * 获取实体类中的属性
     *
     * @param fieldName 属性名
     * @return 如果属性存在, 返回true, 否则返回false
     */
    private boolean isValidField(String fieldName) {
        try {
            UserPO.class.getDeclaredField(fieldName);
            return true;
        } catch (NoSuchFieldException e) {
            return false;
        }
    }

    /**
     * 通过反射初始化Method方法对象
     */
    private void initializeMethodCache() {
        Arrays.stream(UserPO.class.getDeclaredFields()).forEach(field -> {
            String fieldName = field.getName();
            try {
                Method method = UserPO.class.getMethod("get" + StringUtils.capitalize(fieldName));
                methodCache.put(fieldName, method);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("字段 " + fieldName + " 缺少对应的get方法", e);
            }
        });
    }

    /**
     * 打印用户信息
     *
     * @param userPOS 符合条件的用户列表
     * @param fields  实体类的所有属性
     */
    private void printUsers(List<UserPO> userPOS, List<String> fields) {
        userPOS.forEach(userPO -> {
            fields.forEach(field -> {
                try {
                    System.out.print(methodCache.get(field).invoke(userPO) + "\t");
                } catch (Exception e) {
                    System.out.print("N/A\t");
                }
            });
            System.out.println();
        });
    }

    /**
     * 查询用户信息
     *
     * @param miniBalance 最低余额
     */
    public void queryUsers(Integer miniBalance) {
        List<UserPO> userPOS = this.queryUsersWithCondition(miniBalance);
        List<String> fields = this.resolveQueryFields();
        this.printUsers(userPOS, fields);
    }

    @Override
    public List<AddressVO> queryAddrByUserId(Long id) {
        // 用户状态是否异常
        UserPO userPO = getById(id);
        if (userPO == null || userPO.getStatus() != 1L) throw new RuntimeException("当前帐号存在异常");
        // 获取地址信息
        List<AddressPO> addressPOs = Db.lambdaQuery(AddressPO.class).eq(AddressPO::getUserId, id).list();
        if (CollUtil.isNotEmpty(addressPOs)) {
            return BeanUtil.copyToList(addressPOs, AddressVO.class);
        }
        return null;
    }
}
